/* * Copyright (c) 1991-1994 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: audio-sgi.cc,v 1.14 96/04/26 04:08:04 van Exp $ (LBL)"; #include "config.h" #define _BSD_COMPAT 1 #include "Tcl.h" /* * We have AF/audio.h, ./audio.h and /usr/include/audio.h. Crimony. */ #include #include "audio.h" class SGIAudio : public Audio { public: SGIAudio(); virtual int FrameReady(); virtual u_char* Read(); virtual void Write(u_char *); virtual void SetRGain(int); virtual void SetPGain(int); virtual void InputPort(int); virtual void Obtain(); virtual void Release(); protected: int GainClip(int); u_int lastsamp_; u_int lastout_; u_char* buf_; ALport in; ALport out; ALconfig conf; }; extern const u_char lintomulawX[]; extern const short mulawtolin[]; #define AUDIO_MIN_GAIN 0 #define AUDIO_MAX_GAIN 255 static class SGIAudioMatcher : public Matcher { public: SGIAudioMatcher() : Matcher("audio") {} TclObject* match(const char* id) { if (strcasecmp(id, "sgi") == 0) return (new SGIAudio); return (0); } } sgiaudio_matcher; SGIAudio::SGIAudio() { /* open (or create) the lock file */ openlock(); iports = 2; oports = 1; conf = ALnewconfig(); ALsetwidth(conf, AL_SAMPLE_16); ALsetqueuesize(conf, 8000); ALsetchannels(conf, AL_MONO); fd = -1; lastout_ = 0; lastsamp_ = 0; buf_ = new u_char[blksize]; } void SGIAudio::Release() { if (HaveAudio()) { unlock(); unlink(); fd = -1; ALcloseport(in); ALcloseport(out); notify(); } } void SGIAudio::Obtain() { if (HaveAudio()) abort(); if (lock() == 0) { out = ALopenport("vatOut", "w", conf); if (out == NULL) { fprintf(stderr, "vat: couldn't open AL output port.\n"); return; } in = ALopenport("vatIn", "r", conf); if (in == NULL) { fprintf(stderr, "vat: couldn't open AL input port.\n"); return; } ALsetfillpoint(in, 160); long pvbuf[6]; pvbuf[0] = AL_INPUT_RATE; pvbuf[1] = 8000; pvbuf[2] = AL_OUTPUT_RATE; pvbuf[3] = 8000; pvbuf[4] = AL_INPUT_SOURCE; pvbuf[5] = iport? AL_INPUT_LINE : AL_INPUT_MIC; ALsetparams(AL_DEFAULT_DEVICE, pvbuf, 6); fd = ALgetfd(in); SetRGain(rgain); SetPGain(pgain); Audio::Obtain(); } } void SGIAudio::Write(u_char *cp) { register int len = blksize; u_int samps[MAXAUDIOSIZE/2]; register u_int* sp = samps; register u_int* ep = sp + len / 2; register const u_short* u2l = (u_short*)mulawtolin; register u_int* ip = (u_int*)cp; for ( ; sp < ep; sp += 4) { register u_int s = *ip++; sp[0] = (u2l[(s >> 24) & 0xff] << 16) | u2l[(s >> 16) & 0xff]; sp[1] = (u2l[(s >> 8) & 0xff] << 16) | u2l[s & 0xff]; s = *ip++; sp[2] = (u2l[(s >> 24) & 0xff] << 16) | u2l[(s >> 16) & 0xff]; sp[3] = (u2l[(s >> 8) & 0xff] << 16) | u2l[s & 0xff]; } ALwritesamps(out, samps, len); } int SGIAudio::FrameReady() { return (ALgetfilled(in) >= blksize); } u_char* SGIAudio::Read() { register long len = blksize; u_char* cp = buf_; /* * for some reason, SGI didn't bother to filter out the * mike 'phantom power' DC signal (god forbid they should * use the Indigo DSP for anything or invest a dime in * transformer coupling the mike) so we end up with a * large DC offset that screws up the lin-to-mu conversion * and the speakerphone power calculations. So all the * extra junk in the following loop is a low pass filter * to estimate the DC bias & remove it. * * The multiply by 2 on the samples is because SGI maps * stereo to mono by doing (L+R)/2 rather than clip(L+R). * Since the mikes they ship are mono, this effectively * cuts the mike gain by a factor of two. We can't * restore the 1 bit of dynamic range they throw away * but we jack the gain back up where is should be. */ short samps[MAXAUDIOSIZE]; register short* sp = samps; ALreadsamps(in, sp, len); register short* ep = sp + len; register const u_char* l2u = lintomulawX; register u_int* ip = (u_int*)cp; register int smean = lastsamp_; for ( ; sp < ep; sp += 4) { register int mean, dif; register u_int res; register int s0 = sp[0] << 1; register int s1 = sp[1] << 1; register int s2 = sp[2] << 1; register int s3 = sp[3] << 1; mean = smean >> 13; dif = s0 - mean; smean += dif; res = l2u[dif & 0x1ffff] << 24; mean = smean >> 13; dif = s1 - mean; smean += dif; res |= l2u[dif & 0x1ffff] << 16; mean = smean >> 13; dif = s2 - mean; smean += dif; res |= l2u[dif & 0x1ffff] << 8; mean = smean >> 13; dif = s3 - mean; smean += dif; res |= l2u[dif & 0x1ffff]; *ip++ = res; } lastsamp_ = smean; return (cp); } int SGIAudio::GainClip(int level) { if (level < AUDIO_MIN_GAIN) return AUDIO_MIN_GAIN; else if (level > AUDIO_MAX_GAIN) return AUDIO_MAX_GAIN; else return level; } void SGIAudio::SetRGain(int level) { rgain = GainClip(level); if (fd >= 0) { static long atten[] = { 34, 37, 40, 45, 50, 55, 63 }; long pvbuf[4]; int index = ((255 - rgain) * 39) / 255; pvbuf[3] = pvbuf[1] = (index > 32) ? (atten[index-33] << 2) : (index << 2); pvbuf[0] = AL_LEFT_INPUT_ATTEN; pvbuf[2] = AL_RIGHT_INPUT_ATTEN; ALsetparams(AL_DEFAULT_DEVICE, pvbuf, 4); } } void SGIAudio::SetPGain(int level) { pgain = GainClip(level); if (fd >= 0) { long pvbuf[4]; #ifdef SGI_COMPAT float gain = pgain <= 0? 0. : pow(10.0, float(pgain)*(2.406540183/255.)) + 0.5; pvbuf[3] = pvbuf[1] = long(gain); #else pvbuf[3] = pvbuf[1] = long(pgain); #endif pvbuf[0] = AL_LEFT_SPEAKER_GAIN; pvbuf[2] = AL_RIGHT_SPEAKER_GAIN; ALsetparams(AL_DEFAULT_DEVICE, pvbuf, 4); } } void SGIAudio::InputPort(int p) { iport = p; if (fd >= 0) { long pvbuf[2]; pvbuf[0] = AL_INPUT_SOURCE; pvbuf[1] = iport? AL_INPUT_LINE : AL_INPUT_MIC; ALsetparams(AL_DEFAULT_DEVICE, pvbuf, 2); } }