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

audio-af.cc

/*
 * Copyright (c) 1993,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-af.cc,v 1.28 96/04/19 17:25:50 mccanne Locked $ (LBL)";

#include <sys/file.h>

#include "audio.h"
#include <AF/AFlib.h>

struct afstate {
      AC ac;
      int mingain;
      int maxgain;
      int gain;
      int soft;
};

class AFAudio : public Audio {
    public:
      AFAudio();
      virtual int FrameReady();
      virtual u_char* Read();
      virtual     void Write(u_char *);
      virtual void SetRGain(int);
      virtual void SetPGain(int);
      virtual void OutputPort(int);
      virtual void Obtain();
      virtual void Release();
    protected:
      void SendReadRequest();
      int FindDefaultDevice(AFAudioConn*);
      void noserver();
      int slidergain(const afstate& af) const;
      void setgain(int level, afstate& af);
      void chksoftgain(afstate&, int softgain, int mask);

      AFAudioConn* raud;
      AFAudioConn* paud;

      u_char* readptr;
      u_char* readbufend;
      u_char* readbuf;
      u_char* replybuf;
      u_int afblksize;

      u_char* nextframe;
      u_char* firstframe;
      u_char* writebuf;
      int usingbuf;

      int lastmean_[4];

      u_int aftime;           /* time of last frame read */
      u_int wrttime;          /* time of last frame written */
      u_int minusoff;         /* max - diff. between server & writer */
      u_int plusoff;          /* avg + diff. between server & writer */
      int plusvar;            /* avg + variation between server & writer */
      u_int poff;       /* play offset relative to aftime */
      int pmiss;        /* number of consecutive missed frames */

      afstate raf;            /* record context */
      afstate saf;            /* speaker context */
      afstate haf;            /* headphone context */
      afstate *paf;           /* play context (points at saf/haf) */

      enum {
            PHONE_CODEC = 0,
            LOCAL_CODEC = 1,
            HIFI_BOTH = 2,
            HIFI_LEFT = 3,
            HIFI_RIGHT = 4
      };
};

static class AFAudioMatcher : public Matcher {
public:
      AFAudioMatcher() : Matcher("audio") {}
      TclObject* match(const char* id) {
            if (strcasecmp(id, "af") == 0)
                  return (new AFAudio);
            return (0);
      }
} afaudio_matcher;

/*XXX*/
#include "/usr/src/local/AudioFile/AF/lib/AF/Alibint.h"
extern "C" void _AFlush(AFAudioConn* aud);
extern "C" void _ARead(AFAudioConn* aud, char* data, long size);
extern "C" void _AReadPad(AFAudioConn* aud, char* data, long size);
extern "C" AStatus _AReply(AFAudioConn* aud, aReply* rep,
                     int extra, ABool discard);
extern "C" AStatus _AReplyAsync(AFAudioConn* aud, aReply* rep,
                        int extra, ABool discard);

#ifdef __osf__
extern "C" int flock(int, int);
#endif

void AFAudio::chksoftgain(afstate& af, int softgain, int mask)
{
      if (af.mingain == af.maxgain) {
            af.mingain = -30;
            af.maxgain = 30;
            af.soft = 1;
      } else {
            af.soft = 0;
            if (softgain != 0) {
                  AFSetACAttributes attr;
                  attr.rec_gain = softgain;
                  AFChangeACAttributes(af.ac, mask, &attr);
            }
      }
}

AFAudio::AFAudio()
{
      Tcl& tcl = Tcl::instance();
      int device = atoi(tcl.attr("afDevice"));
      int blocks = atoi(tcl.attr("afBlocks"));
      int rgain = atoi(tcl.attr("afSoftInputGain"));
      int pgain = atoi(tcl.attr("afSoftOuputGain"));

      /*
       * if the AUDIOFILE environment variable is set, use it as
       * the server name.  Otherwise AFOpenAudioConn will try
       * to use DISPLAY which is probably wrong so force ":0".
       */
      const char* sname = getenv("AUDIOFILE");
      if (sname == 0)
            sname = ":0";
      raud = AFOpenAudioConn((char*)sname);
      if (raud == 0)
            noserver();
      paud = AFOpenAudioConn((char*)sname);
      if (paud == 0)
            noserver();

      if (device >= ANumberOfAudioDevices(raud)) {
            fprintf(stderr, "vat: AF: bad device %d", device);
            exit(1);
      }
      if (device < 0) {
            device = FindDefaultDevice(raud);
            if (device < 0) {
                  fprintf(stderr, "vat: AF: cannot find ulaw device");
                  exit(1);
            }
      }

      /* set up audio context, find sample size and sample rate */
      
      AFSetACAttributes attr;
      attr.type = MU255;
      raf.ac = AFCreateAC(raud, device, ACEncodingType, &attr);
      saf.ac = AFCreateAC(paud, device, ACEncodingType, &attr);
#ifdef notyet
      haf.ac = AFCreateAC(paud, HIFI_LEFT, 0, 0);
#endif
      paf = &saf;
      raf.gain = AFQueryInputGain(raf.ac, &raf.mingain, &raf.maxgain);
      chksoftgain(raf, rgain, ACRecordGain);
      saf.gain = AFQueryOutputGain(saf.ac, &saf.mingain, &saf.maxgain);
      chksoftgain(saf, pgain, ACPlayGain);
#ifdef notyet
      haf.gain = AFQueryOutputGain(haf.ac, &haf.mingain, &haf.maxgain);
      chksoftgain(haf);
#endif
      /*
       * Set midscale initial values.  Since the server won't tell us
       * what the real initial value is, and we want the slider position
       * to reflect the startup value, we have no other choice.
       */
      SetRGain(128);
      SetPGain(128);

      afblksize = blksize * blocks;
      replybuf = new u_char[afblksize + sizeof(aReply)];
      readbuf = replybuf + sizeof(aReply);
      readptr = readbufend = readbuf + afblksize;

      nextframe = 0;
      firstframe = 0;
      usingbuf = 0;
      if (afblksize != blksize)
            writebuf = new u_char[afblksize];
      else
            writebuf = 0;

      poff = 3 * afblksize;
      plusoff = poff << 5;
      plusvar = 0;

      lastmean_[0] = 0;
      lastmean_[1] = 0;
      lastmean_[2] = 0;
      lastmean_[3] = 0;

      /* open (or create) the lock file */
      openlock();
}

void AFAudio::noserver()
{
      if (getenv("AUDIOFILE") == 0) {
            fprintf(stderr,
                  "vat: can't connect to AF server (AUDIOFILE not set)");
      } else
            fprintf(stderr, "can't connect to AF server");
      exit(1);
}

/* Find a suitable default device (the first device not connected to the phone)
 * Returns device number or -1 if no suitable device can be found.
 */
int
AFAudio::FindDefaultDevice(AFAudioConn* aud)
{
      char *s = (char *)getenv("AF_DEVICE");
      if (s != NULL)
            return (atoi(s));

      /* Find the first non-phone, 8kHz, mono device */
      int n = ANumberOfAudioDevices(aud);
      for (int i = 0; i < n; ++i) {
            AFDeviceDescriptor* a = AAudioDeviceDescriptor(aud, i);
            if (a->inputsFromPhone == 0 && a->outputsToPhone == 0 &&
                a->playSampleFreq == 8000 && a->playNchannels == 1)
                  return (i);
      }
      return (-1);
}

void AFAudio::Release()
{
      if (HaveAudio()) {
            /* gobble the result of the in-progress read */
            aRecordSamplesReply reply;
            _AReply(raud, (aReply*)&reply, 0, aFalse);
            if (reply.length * 4 == afblksize) {
                  char dummy[512];
                  _AReadPad(raud, dummy, afblksize);
            }
            unlock();
            unlink();
            fd = -1;
            notify();
      }
}

void AFAudio::Obtain()
{
      if (HaveAudio())
            abort();

      if (lock() == 0) {
            /* audio is ours - kick off first read */
            fd = raud->fd;
            aftime = AFGetTime(raf.ac);
            wrttime = 0;
            minusoff = 0;
            readptr = readbufend;
            SendReadRequest();
            Audio::Obtain();
      }
}

void AFAudio::Write(u_char *cp)
{
      if (HaveAudio()) {
            if (afblksize != blksize) {
                  if (nextframe == 0) {
                        firstframe = cp;
                        nextframe = cp + blksize;
                        usingbuf = 0;
                        return;
                  }
                  if (nextframe != cp) {
                        if (! usingbuf) {
                              /*
                               * frames wrapped in ss buffer --
                               * copy to writebuf to keep things
                               * contiguous.
                               */
                              int curlen = nextframe - firstframe;
                              memcpy(writebuf, firstframe, curlen);
                              firstframe = writebuf;
                              nextframe = writebuf + curlen;
                              usingbuf = 1;
                        }
                        memcpy(nextframe, cp, blksize);
                  }
                  nextframe += blksize;
                  u_int len = nextframe - firstframe;
                  if (len < afblksize)
                        return;

                  cp = firstframe;
                  nextframe = 0;
            }
            u_int at = aftime + poff;
            if (at - wrttime > 3 * afblksize && wrttime) {
                  /*
                   * start of talk after silence -- see if we
                   * should adjust offset.  If AF missed any
                   * frames in the last talkspurt, adjust the
                   * offset to one that wouldn't have missed any
                   * frames.  Otherwise if we're more than a
                   * frame time ahead of the recent average offset,
                   * drop the current offset by half the difference
                   * (or the max that wouldn't reorder AF playout,
                   * whichever is smaller).
                   */
                  u_int noff = poff;
                  if (minusoff) {
                        noff = minusoff >> 2;
                        minusoff = 0;
                  } else {
                        /*
                         * we went through the last talkspurt
                         * with no drops & an average backlog
                         * variation between us & AF of 'plusvar'.
                         * To avoid drops we need 2*afblksize +
                         * 2*plusvar of buffer between us & AF.
                         * If we have more than that, reduce it.
                         */
                        u_int doff = (plusvar >> 2) + (2 * afblksize);
                        if (doff < noff) {
                              int adj = (noff - doff) >> 2;
                              noff -= adj;
                              if (int(noff) < int(wrttime - aftime))
                                    noff = wrttime - aftime;
                        }
                  }
                  if (noff != poff) {
                        poff = (noff + 3) & ~3;
                        at = aftime + noff;
                  }
            }
            wrttime = at;
            u_int now = AFPlaySamples(paf->ac, at, afblksize, cp);
            int dif = now - at;
            if (dif > 0) {
                  u_int noff = now - aftime + 2 * afblksize;
                  if (minusoff)
                        minusoff += noff - (minusoff >> 1);
                  else
                        minusoff = noff << 1;
                  if (++pmiss >= 3) {
                        /*
                         * losing bad - adapt now rather than
                         * waiting for next talkspurt.
                         */
                        poff = ((minusoff >> 1) + 3) & ~3;
                        pmiss = 0;
                        minusoff = 0;
                  }
            } else {
                  int delta = dif + (plusoff >> 5);
                  plusoff -= delta;
                  if (delta < 0)
                        delta = -delta;
                  plusvar += delta - (plusvar >> 3);
                  pmiss = 0;
            }
      }
}

void AFAudio::SendReadRequest()
{
        register aRecordSamplesReq *req;

      if (HaveAudio()) {
#define aud raud
            GetReq(RecordSamples, req);
#undef aud
            req->ac = raf.ac->acontext;
            req->startTime = aftime;
            req->nbytes = afblksize;
            req->sampleType = raf.ac->attributes.type;
            req->nchannels = raf.ac->attributes.channels;
            req->mask = ABlockMask;
            if (raf.ac->attributes.endian == ABigEndian)
                  req->mask |= ABigEndianMask;
            _AFlush(raud);
      }
}

int AFAudio::FrameReady()
{
      u_char* cp = readptr;
      if (cp >= readbufend) {
            aRecordSamplesReply* reply = (aRecordSamplesReply*)replybuf;
            if (_AReplyAsync(raud, (aReply*)reply, afblksize >> 2, aFalse) <= 0)
                  /* no data available */
                  return (0);

            /* queue the next read */
            SendReadRequest();
            /*
             * If we get too far behind or get confused &
             * think we're ahead, jump forward.  (This can
             * easily happen if the process is suspended.)
             */
            u_int srvtime = reply->currentTime;
            u_int dif = srvtime - aftime;
            if (dif > 16000)
                  if (int(dif) < -1600 || int(dif) > 0) {
                        aftime = srvtime;
                  }
            aftime += afblksize;
            readptr = readbuf;
      }
      return (1);
}

extern const unsigned char lintomulaw[];
extern const short mulawtolin[];

u_char* AFAudio::Read()
{
      u_char* cp = readptr;
      readptr = cp + blksize;

      /*
       * remove any dc bias from the input signal.
       */
      register u_char* ip = cp;
      register u_char* ep = readptr;
      register int smean = lastmean_[iport];
      register const short* u2l = mulawtolin;
      register const u_char* l2u = lintomulaw;
      while (ip < ep) {
            register int mean, dif;
            register int s0 = u2l[ip[0]];
            register int s1 = u2l[ip[1]];
            register int s2 = u2l[ip[2]];
            register int s3 = u2l[ip[3]];

            mean = smean >> 13;
            dif = s0 - mean;
            smean += dif;
            ip[0] = l2u[dif & 0xffff];

            mean = smean >> 13;
            dif = s1 - mean;
            smean += dif;
            ip[1] = l2u[dif & 0xffff];

            mean = smean >> 13;
            dif = s2 - mean;
            smean += dif;
            ip[2] = l2u[dif & 0xffff];

            mean = smean >> 13;
            dif = s3 - mean;
            smean += dif;
            ip[3] = l2u[dif & 0xffff];

            ip += 4;
      }
      return (cp);
}

int AFAudio::slidergain(const afstate& af) const
{
      if (af.mingain == 0 && af.maxgain == 0)
            /* Not adjustable.  Just maintain mid-scale */
            return (128);

      float range = af.maxgain - af.mingain;
      return (int(255. * float(af.gain - af.mingain) / range));
}

void AFAudio::setgain(int level, afstate& af)
{
      float range = af.maxgain - af.mingain;
      af.gain = int(af.mingain + range * float(level) / 255.);
}

void AFAudio::SetRGain(int level)
{
      setgain(level, raf);
      if (raf.soft) {
            AFSetACAttributes attr;
            attr.rec_gain = raf.gain;
            AFChangeACAttributes(raf.ac, ACRecordGain, &attr);
      } else
            AFSetInputGain(raf.ac, raf.gain);
      rgain = slidergain(raf);
}

void AFAudio::SetPGain(int level)
{
      setgain(level, *paf);
      if (paf->soft) {
            AFSetACAttributes attr;
            attr.play_gain = paf->gain;
            AFChangeACAttributes(paf->ac, ACPlayGain, &attr);
      } else
            AFSetOutputGain(paf->ac, paf->gain);
      pgain = slidergain(*paf);
}

void AFAudio::OutputPort(int p)
{
      oport = p;
#ifdef notyet
      paf = oport? &haf : &saf;
#else
      paf = &saf;
#endif
}

Generated by  Doxygen 1.6.0   Back to index