/* * Copyright (c) 1991-1993 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the Computer Systems * Engineering Group at Lawrence Berkeley Laboratory. * 4. Neither the name of the University nor of the Laboratory may be used * to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ static const char rcsid[] = "@(#) $Header: ss.cc,v 1.12 96/03/23 04:19:29 van Exp $ (LBL)"; #include "config.h" #include "ss.h" extern "C" const u_char mulawsum[256*256]; extern "C" const u_char mugaintab[64*256]; static inline int ABS(int i) { return (i < 0? -i : i); } #define ALL_ZERO ((ULAW_ZERO<<24)|(ULAW_ZERO<<16|(ULAW_ZERO<<8))|ULAW_ZERO) #define AGC_FILTER 4 /* 2^4 = 320ms time const. */ #define AGC_CUTOFF 16 #define AGC_GAIN (AGC_FILTER + 3) #define AGC_DEADTIME (8*160) /* adjust every 160ms (time in samples) */ SampleStream::SampleStream(int size, int n, int p, int l) : samples(new u_char[n * size]), zero(new u_char[size]), now(p + size), sclock(p + size + random()), past(p + size), blksize(size), totsize(n * size), maxsamp((n - 1) * size - p), sltmean(0), smean(0), smax(0), doAGC(0), bias(0), oldbias(0), AGClevel(l), AGCerr(0), deadtime(0), ssthresh_(20) { if (maxsamp < (int)blksize) abort(); memset((void *)zero, ULAW_ZERO, blksize); memset(samples, ULAW_ZERO, totsize); } SampleStream::SampleStream(const SampleStream& s) : samples(new u_char[s.totsize]), zero(new u_char[s.blksize]), now(s.past), sclock(s.past + random()), past(s.past), blksize(s.blksize), totsize(s.totsize), maxsamp(s.maxsamp), sltmean(0), smean(0), smax(0), doAGC(0), bias(0), oldbias(0), AGClevel(s.AGClevel), AGCerr(0), deadtime(0), ssthresh_(s.ssthresh_) { memset((void *)zero, ULAW_ZERO, blksize); memset(samples, ULAW_ZERO, totsize); } void SampleStream::Compute() { /* * compute short term max power (max of 8 sample means) and * long term power (full frame mean passed through simple * lowpass filter). This is done for every input & output * frame so it should be reasonably efficient. We take advantage * of the fact that frames are 8-byte aligned (at least) and * of the u-law sign-magnitude representation to do several * sums 'in parallel'. */ register int b = blksize; register int amt = b >> 3, max = 0, sum = 0; register const u_int *ip = (const u_int*)&samples[now]; register int mask = 0x007f007f; do { register int s0 = ~ip[0]; register int s1 = ~ip[1]; ip += 2; register int p; p = s0 & mask; p += (s0 >> 8) & mask; p += s1 & mask; p += (s1 >> 8) & mask; p = ((p >> 16) + p) & 0x3ff; if (p > max) max = p; sum += p; } while (--amt > 0); smax = max; sum /= b; smean = sum; register int sm = sltmean; register int ssm = sm >> MEAN_FILTER; sm += sum - ssm; sltmean = sm; } void SampleStream::_UpdateAGC() { register int sm = sltmean; if (sm >= (AGC_CUTOFF << MEAN_FILTER) && smax - sm >= (ssthresh_ << MEAN_FILTER)) { register int ssm = sm >> MEAN_FILTER; register int err = AGClevel - ssm; AGCerr += err - (AGCerr >> AGC_FILTER); register int abserr = ABS(AGCerr); if (int(sclock - deadtime) >= 0 && (abserr >> (AGC_GAIN-1)) > 0) { deadtime = sclock + AGC_DEADTIME; AdjustScale((AGCerr + AGC_GAIN/2) >> AGC_GAIN); } } } static inline const u_int* doMix(u_int* sp, const u_int* ip, int len) { const u_char* const mixtab = mulawsum; register u_int bmask = 0xff00; register u_int allzero = ALL_ZERO; for (register u_int* ep = sp + (len >> 2); sp < ep; ++ip, ++sp) { register u_int mixsum; register u_int idat = *ip; register u_int sdat = *sp; // special case all zeros since they happen often if (idat == allzero) continue; if (sdat == allzero) mixsum = idat; else { register u_int sb = (sdat >> 16) & bmask; register u_char ib = idat >> 24; mixsum = mixtab[sb | ib] << 24; sb = (sdat >> 8) & bmask; ib = idat >> 16; mixsum |= mixtab[sb | ib] << 16; sb = sdat & bmask; ib = idat >> 8; mixsum |= mixtab[sb | ib] << 8; sb = (sdat << 8) & bmask; ib = idat; mixsum |= mixtab[sb | ib]; } *sp = mixsum; } return (ip); } static inline const u_int* doBiasedMix(u_int* sp, const u_int* ip, int len, int b) { const u_char* const mixtab = mulawsum; const u_char* const gaintab = &mugaintab[(b + 32) << 8]; register u_int bmask = 0xff00; register u_int allzero = ALL_ZERO; for (register u_int* ep = sp + (len >> 2); sp < ep; ++ip, ++sp) { register u_int mixsum; register u_int idat = *ip; register u_int sdat = *sp; // special case all zeros since they happen often if (idat == allzero) continue; register u_int sb = (sdat >> 16) & bmask; register u_char ib = idat >> 24; mixsum = mixtab[sb | gaintab[ib]] << 24; sb = (sdat >> 8) & bmask; ib = idat >> 16; mixsum |= mixtab[sb | gaintab[ib]] << 16; sb = sdat & bmask; ib = idat >> 8; mixsum |= mixtab[sb | gaintab[ib]] << 8; sb = (sdat << 8) & bmask; ib = idat; mixsum |= mixtab[sb | gaintab[ib]]; *sp = mixsum; } return (ip); } #ifdef COMPRESSION static inline const u_int* doCompressedMix(u_int* sp, const u_int* ip, int len, int level) { int power = 0; int i = len / 4; register int mask = 0x007f007f; const u_int *p = ip; do { register int s = ~*p++; power += s & mask; s >>= 8; power += s & mask; } while (--i > 0); double pin = (power >> 16) + (power & 0xffff); pin = pin / (16 * len) + 4.94692243; extern double CompressionSlope; double pout = pin * CompressionSlope + (1 - CompressionSlope) * double(level) / 6.4; int gain = irint(32. / 4. * (pout - pin)); if (gain < -32) gain = -32; else if (gain > 31) gain = 31; return doBiasedMix(sp, ip, len, gain); } #endif void SampleStream::Mix(int delta, const u_char *in, int len) { register u_int off; register const u_int* ip = (const u_int*)in; off = now + delta; if (off >= totsize) off -= totsize; register u_int* sp = (u_int*)&samples[off]; if (off + len > totsize) { /* * The thing we're mixing in overlaps the wrap at the * end of the stream -- do the first piece (the one * that goes to the end of the stream). */ register int i = totsize - off; len -= i; #ifndef COMPRESSION if (bias) ip = doBiasedMix(sp, ip, i, bias); #else if (doAGC) ip = doCompressedMix(sp, ip, i, AGClevel); #endif else ip = doMix(sp, ip, i); sp = (u_int*)samples; } #ifndef COMPRESSION if (bias) doBiasedMix(sp, ip, len, bias); #else if (doAGC) doCompressedMix(sp, ip, len, AGClevel); #endif else doMix(sp, ip, len); }