Logo Search packages:      
Sourcecode: vat version File versions  Download package

audio-sgi.cc

/*
 * 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 </usr/include/audio.h>
#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);
      }
}

Generated by  Doxygen 1.6.0   Back to index