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

audio-ibm.cc

//-----------------------------------------------------------------------------
// COPYRIGHT BY OERTEL & ZAHL SOFTWAREENTWICKLUNG GbR, BERLIN, GERMANY
//    file:       audio-ibm.cc
//    written by: Christian Zahl
//    description:      Audio class for the IBM RS/6000 familiy using the
//                Ultimedia Audio Adapter (or the build-in in the
//                43P)
//    creation date:    1996/03/13
//
// $Id: audio-ibm.cc,v 1.4 1996/03/25 08:30:15 czahl Exp $
//
// Revision 1.4  1996/03/25  08:30:15  czahl
// - forgott to disable the hardware monitor after opening the audio device.
//
// Revision 1.3  1996/03/14  20:01:06  czahl
// - minor bug: the recFd and playFd were not initialized in IBMAudio::Obtain
// - minor cosmetic changes
//
// Revision 1.2  1996/03/14  10:59:58  czahl
// - this is the first alpha version for Mimi.
// - now we also added the other in- and output ports.
//
// Revision 1.1  1996/03/14  08:39:46  czahl
// Initial revision
// - After downloading the merged version of Vat with the NeVoT audio driver
//   support on 1996/03/13, I decided to hack my own real class for the
//   IBM audio decive. At midnight, we had the first stable version running
//   (I listened more than an hour to an Eurythmics CD via the loopback. :-)
//------------------------------------------------------------------------------
static const char rcsid[] =
      "@(#) $Header: audio-ibm.cc,v 1.3 96/05/01 21:04:27 van Exp $";

/*-----     standard includefiles ----------------------------------------------*/
#include <osfcn.h>
#include <stdio.h>
#include <errno.h>
#include <math.h>
#define _BSD_COMPAT 1
#include <sys/file.h>
#include <sys/audio.h>
#include <sys/acpa.h>

/*-----     user includefiles --------------------------------------------------*/
#include "Tcl.h"
#include "audio.h"

/*-----     defines ------------------------------------------------------------*/
#define     WITH_TRACE        1
#if WITH_TRACE
#define     TRACE(xxx)        xxx
#else
#define TRACE(xxx)
#endif

#define AUDIO_MIN_GAIN        0
#define AUDIO_MAX_GAIN        255

/*
 * Because we need some more definitions than normaly found in the "offical"
 * include file (sys/audio.h), we declare them here, if they heven't been
 * declared before.
 */
#ifndef AUDIO_MODIFY_LIMITS
#define AUX1                  100
#define AUDIO_SET_GAIN        14
#define INTERNAL_SPEAKER_ON   10
#define INTERNAL_SPEAKER_OFF  11
#define AUDIO_MODIFY_LIMITS   20
typedef struct _audio_set_gain
{
      signed long left_gain;
      signed long right_gain;
} audio_set_gain;
typedef struct _init_buf_req
{
      long buffer_size;
      long play_block_count;
      long play_lower_limit;
      long cap_upper_limit;
      long byte_count;
      long return_code;
} init_buf_req;
#endif /* AUDIO_MODIFY_LIMITS */

/*-----     type definitions ---------------------------------------------------*/

/*-----     local functions ----------------------------------------------------*/

/*-----     global functions ---------------------------------------------------*/

/*-----     local variables ----------------------------------------------------*/

/*-----     global variables ---------------------------------------------------*/
extern const unsigned char    lintomulawX[];
extern const unsigned char    lintomulaw[];
extern const short            mulawtolin[];

/*------------------------------------------------------------------------*/
class IBMAudio : public Audio {
public:
                        IBMAudio();
  virtual u_char*       Read();
  virtual void                Write(u_char *);
  virtual void                SetRGain(int);
  virtual void                SetPGain(int);
  virtual void                Obtain();
  virtual void                Release();
  virtual int                 FrameReady ();
  virtual void                InputPort (int);
  virtual void                OutputPort (int);
  virtual void                SetMonitor (int);
protected:
      void              InitAudioChange ();
      void              InitAudioControl ();
      void              InitAudioDevice ();
      int               GainClip(int);
      int               recFd;
      int               playFd;
        audio_buffer            bufStat;
      audio_init        audioInit;
      audio_change            audioChange;
      audio_control           audioControl;
        init_buf_req            bufReq;
      u_char                  *playBuf;               /* needed for delay reduction */
      short             *recBuf;                /* buffer for holding captured data */
      int               recBufSize;             /* # of bytes in the buffer */
      u_char                  *readBuf;               /* buffer for delivering the "read" data */
      int               lastmean;
      int               contHighWaters;               /* # on continous hiwater marks reached */
};
/*------------------------------------------------------------------------*/
static class IBMAudioMatcher : public Matcher {
public:
      IBMAudioMatcher() : Matcher ("audio") {}
      TclObject* match (const char* id) {
            if (strcasecmp (id, "ibm") == 0)
                  return (new IBMAudio);
            return 0;
      }
} ibmaudio_matcher;
/*------------------------------------------------------------------------*/
IBMAudio::IBMAudio()
{
      TRACE (fprintf (stderr, "IBMAudio\n");)
/* open (or create) the lock file */
      openlock();
      iports = 4;
      oports = 4;
      fd = -1;
      recFd = -1;
      playFd = -1;
      playBuf = new u_char[blksize];
      readBuf = new u_char[blksize];
      recBuf = new short[blksize];
} /* IBMAudio::IBMAudio */
/*------------------------------------------------------------------------*/
void IBMAudio::Obtain()
{
static      char              *recFn[] = {"/dev/baud0/1", "/dev/paud0/1", NULL};
static      char              *playFn[] = {"/dev/baud0/2", "/dev/paud0/2", NULL};
      int               i;

      TRACE (fprintf (stderr, "IBMAudio::Obtain\n");)
      if (HaveAudio())
            abort();
      if (lock() != 0)
            return;
      recBufSize = 0;
      contHighWaters = 0;
/*** search an valid audio device and open it ***/
      for (i=0; recFn[i] != NULL; i++)
            if ((recFd = open (recFn[i], O_RDONLY | O_NDELAY)) >= 0)
                  break;
      if (recFn[i] == NULL) {
            fprintf(stderr, "vat: couldn't open any record port.\n");
            return;
            }
      TRACE (fprintf (stderr, "IBMAudio::Obtain %s %s\n", recFn[i], playFn[i]);)
      if ((playFd = open (playFn[i], O_WRONLY | O_NDELAY)) < 0) {
            close (recFd);
            recFd = -1;
            fprintf (stderr, "vat: cannot open playback port!\n");
            return;
            }
/*** initialize the device ***/
      InitAudioDevice ();
      fd = recFd;
      Audio::Obtain();
} /* IBMAudio::Obtain */
/*------------------------------------------------------------------------*/
void IBMAudio::Release()
{
      TRACE (fprintf (stderr, "IBMAudio::Release\n");)
      if (HaveAudio()) {
            unlock ();
            unlink ();
            close (recFd);
            close (playFd);
            recFd = -1;
            playFd = -1;
            fd = -1;
            notify ();
            }
} /* IBMAudio::IBMAudioRelease */
/*------------------------------------------------------------------------*/
int IBMAudio::FrameReady ()
/*
 * Because the IBM audio device does not deliver blocks of 160 samples, but
 * frames of 2^n, we have to buffer the data, so that vat thinks we are
 * reading 160 samples.
 * Keep in mind, that we are recording with 16 linear at 8000 Hz, because we
 * want to reduce the DC offset.
 */
{
      int               n;

      n = blksize -recBufSize;                        /* samples missing to 160 */
      if (n <= 0)
            return 1;
      n *= 2;                                         /* 2 bytes / sample! */
      if ((n = read (recFd, recBuf +recBufSize, n)) <= 0)
            return 0;
      n /= 2;                                         /* we need the # of samples, not bytes! */
      recBufSize += n;
      if (recBufSize >= blksize)
            return 1;
      else
            return 0;
} /* IBMAudio::FrameRead */
/*------------------------------------------------------------------------*/
u_char* IBMAudio::Read()
/*
 * Because it seems that the AIX Audio Adapter also has a DC offset,
 * we use the same method to kill the DC level as in the SGI driver.
 */
{
register int                  smean = lastmean;
register int                  mean, dif, res;
register int                  r;
      int               i;

      for (i=0; i<blksize; i++) {
            r = (int(recBuf[i])) << 1;
            mean = smean >> 13;
            dif = r - mean;
            smean += dif;
            readBuf[i] = lintomulawX[dif & 0x1ffff];
            } /* for */
      lastmean = smean;
      recBufSize -= blksize;                          /* should be = 0 */
      return readBuf;                                 /* return the data */
} /* IBMAudio::Read */
/*------------------------------------------------------------------------*/
void IBMAudio::Write (u_char *cp)
/*
 * Write the audio samples the the audio device.
 * Because of the nature of the IBM audio device (don't know how it is on
 * other platforms) it can be possible that the dalay increases to an
 * very bad amount of audio data (up a second), specially on slow machines
 * like my M20. The reason is, that if the process will be descheduled
 * for a time longer than data are available for playback, the buffer
 * gets empty. In this state, the audio device STOPS to play any data.
 * When the process get rescheduled again, it receives a bulk of captured
 * audio data (capturing is NOT stoped). Because this acts as a trigger
 * for writing the audio data, the same bulk of data will be written to
 * the audio device, resulting in a high delay.
 *
 * What I do is the same as I have done in the NeVoT audio driver for AIX.
 * I reduce the delay by ommiting some samples, so that the delay will
 * be reduced by time. Of course, this results in a short frequency shift,
 * but you don't hear it very hard. But the result is still acceptable,
 * not for continous tones, but for voice and music :-)
 *
 * In fact, now I have implemented a variation of the algorithm. Now we not
 * only ommit some samples, but perform a linear aproximation for the new
 * samples by combining several samples (something like dithering). By doing
 * so, the quality is much better, for voice, music and for tones also!
 */
{
#define     HIGHWATER         (150*8)                       /* 150ms */
register int                  i;
register int                  n;
register int                  v;

      if (ioctl (playFd, AUDIO_BUFFER, &bufStat) == -1)
            perror ("IBMAudio::Write AUDIO_BUFFER");
      if (bufStat.write_buf_size >= HIGHWATER)
            contHighWaters++;
      else
            contHighWaters = 0;
      if (contHighWaters > 3) {                       /* after 3 continous "overflows" */
/*
            fprintf (stderr, "%d ", bufStat.write_buf_size >> 3);
            fflush (stderr);
*/
#ifdef OLD
            for (i=0, n=0; i<blksize; i++)
                  if (i % 32)                   /* ommit every 20th sample (2.5ms) */
                        playBuf[n++] = cp[i];
#else
            for (n=0, i=0;i <(blksize-1)*1000; i+=1025) {   /* shorten the samples by 2.5% */
                  v = (int(mulawtolin[cp[i /1000]])) *(1000 -(i %1000)) /1000;
                  v+= (int(mulawtolin[cp[i /1000 +1]])) *(i %1000) /1000;
                  playBuf[n++] = lintomulaw[(unsigned short)v];
                  } /* for */
#endif
            write (playFd, playBuf, n);
            } /* if */
      else
            write (playFd, cp, blksize);
} /* IBMAudio::Write */
/*------------------------------------------------------------------------*/
int IBMAudio::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;
} /* IBMAudio::GainClip */
/*------------------------------------------------------------------------*/
void IBMAudio::SetRGain (int level)
{
        audio_set_gain          audioSetGain;

      rgain = GainClip (level);
      if (recFd < 0)
            return;
      audioSetGain.left_gain = rgain *100 /AUDIO_MAX_GAIN;
      audioSetGain.right_gain = rgain *100 /AUDIO_MAX_GAIN;
        if (ioctl (recFd, AUDIO_SET_GAIN, &audioSetGain) == -1)
            perror ("IBMAudio::SetRGain");
} /* IBMAudio::SetRGain */
/*------------------------------------------------------------------------*/
void IBMAudio::SetPGain (int level)
{
      pgain = GainClip(level);
      if (playFd < 0)
            return;
        InitAudioChange ();
        InitAudioControl ();
        audioChange.volume = 0x7fff0000 /AUDIO_MAX_GAIN *pgain;
        audioChange.volume_delay = 0;
        audioControl.request_info = &audioChange;
        audioControl.ioctl_request = AUDIO_CHANGE;
        if (ioctl (playFd, AUDIO_CONTROL, &audioControl) == -1)
            perror ("IBMAudio::SetPGain");
} /* IBMAudio::SetPGain */
/*--------------------------------------------------------------------*/
void IBMAudio::InputPort (int p)
{
      iport = p;
      if (recFd < 0)
            return;
/*** select the input port ***/
      InitAudioChange ();
      InitAudioControl ();
      switch (iport) {
            case input_mike:  audioChange.input = LOW_GAIN_MIKE; break;
            case input_line:  audioChange.input = LINE_1; break;
            case input_line2: audioChange.input = LINE_2; break;
            case input_line3: audioChange.input = HIGH_GAIN_MIKE; break;
        } /* switch */
      audioControl.request_info = &audioChange;
      audioControl.ioctl_request = AUDIO_CHANGE;
      if (ioctl (recFd, AUDIO_CONTROL, &audioControl) == -1)
            perror ("IBMAudio::InputPort");
} /* IBMAudio::InputPort */
/*--------------------------------------------------------------------*/
void IBMAudio::OutputPort (int p)
{
      oport = p;
      if (playFd < 0)
            return;
/*** select the output port ***/
      InitAudioChange ();
      InitAudioControl ();
      switch (oport) {
            case output_speaker:    audioChange.output = EXTERNAL_SPEAKER; break;
            case output_phones:     audioChange.output = EXTERNAL_SPEAKER; break;
            case output_line: audioChange.output = OUTPUT_1; break;
            case output_line2:      audioChange.output = AUX1; break;
        } /* switch */
      audioControl.request_info = &audioChange;
      audioControl.ioctl_request = AUDIO_CHANGE;
      if (ioctl (recFd, AUDIO_CONTROL, &audioControl) == -1)
            perror ("IBMAudio::OutputPort");
/*** internal speaker can only be toggled ***/
      InitAudioChange ();
      InitAudioControl ();
      audioControl.request_info = &audioChange;
      audioControl.ioctl_request = AUDIO_CHANGE;
      if (oport == output_speaker)
            audioChange.output = INTERNAL_SPEAKER_ON;
        else
            audioChange.output = INTERNAL_SPEAKER_OFF;
      if (ioctl (recFd, AUDIO_CONTROL, &audioControl) == -1)
            perror ("IBMAudio::OutputPort internal speaker");
} /* IBMAudio::OutputPort */
/*--------------------------------------------------------------------*/
void IBMAudio::InitAudioDevice ()
{
/*** initialize the recording part ***/
      audioInit.srate =       8000;
      audioInit.bits_per_sample =   8 *2;
      audioInit.bsize =       256 *2;
      audioInit.mode =        PCM;
      audioInit.channels =          1;
      audioInit.position_resolution = AUDIO_IGNORE;
      audioInit.flags =       BIG_ENDIAN;
      audioInit.operation =         RECORD;
      audioInit.reserved =          NULL;
      if (ioctl (recFd, AUDIO_INIT, &audioInit) == -1)
            perror ("IBMAudio::InitAudioDevice record");
      InputPort (iport);
      SetRGain (rgain);
      SetMonitor (0);
/*** select the kernel buffersize for recording ***/
      bufReq.buffer_size =          8000 *2 *2;       /* for 2 seconds */
      bufReq.play_block_count =     AUDIO_IGNORE;
      bufReq.play_lower_limit =     AUDIO_IGNORE;
      bufReq.cap_upper_limit =      AUDIO_IGNORE;
      bufReq.byte_count =           AUDIO_IGNORE;
      bufReq.return_code =          0xDeadBeaf;
      if (ioctl (recFd, AUDIO_MODIFY_LIMITS, &bufReq) == -1)
            perror ("IBMAudio::InitAudioDevice record buffer");
/*** initialize the playback part ***/
      audioInit.srate =       8000;
      audioInit.bits_per_sample =   8;
      audioInit.bsize =       512;
      audioInit.mode =        MU_LAW;
      audioInit.channels =          1;
      audioInit.position_resolution = AUDIO_IGNORE;
      audioInit.flags =       FIXED;
      audioInit.operation =         PLAY;
      audioInit.reserved =          NULL;
      if (ioctl (playFd, AUDIO_INIT, &audioInit) == -1)
            perror ("IBMAudio::InitAudioDevice playback");
      OutputPort (oport);
      SetPGain (pgain);
/*** start the both device parts ***/
      InitAudioControl ();
      audioControl.ioctl_request = AUDIO_START;
      if (ioctl (recFd, AUDIO_CONTROL, &audioControl) != 0)
            perror ("IBMAudio::InitAudioDevice start record");
      InitAudioControl ();
      audioControl.ioctl_request = AUDIO_START;
      if (ioctl (playFd, AUDIO_CONTROL, &audioControl) != 0)
            perror ("IBMAudio::InitAudioDevice start playback");
} /* IBMAudio::InitAudioDevice */
/*------------------------------------------------------------------------*/
void IBMAudio::SetMonitor (int on)
{
      IBMAudio::InitAudioChange ();
      IBMAudio::InitAudioControl ();
      if (on)
            audioChange.monitor = MONITOR_UNCOMPRESSED;
      else
            audioChange.monitor = MONITOR_OFF;
      audioControl.request_info = &audioChange;
      audioControl.ioctl_request = AUDIO_CHANGE;
      if (ioctl (recFd, AUDIO_CONTROL, &audioControl) < 0)
            perror ("IBMAudio::SetMonitor ioctl");
} /* IBMAudio::SetMonitor */
/*--------------------------------------------------------------------*/
void IBMAudio::InitAudioChange ()
{
      audioChange.dev_info =        NULL;
      audioChange.input =           AUDIO_IGNORE;
      audioChange.output =          AUDIO_IGNORE;
      audioChange.monitor =         AUDIO_IGNORE;
      audioChange.volume =          AUDIO_IGNORE;
      audioChange.volume_delay =    AUDIO_IGNORE;
      audioChange.balance =         AUDIO_IGNORE;
      audioChange.balance_delay =   AUDIO_IGNORE;
      audioChange.treble =          AUDIO_IGNORE;
      audioChange.bass =            AUDIO_IGNORE;
      audioChange.pitch =           AUDIO_IGNORE;
} /* IBMAudio::InitAudioChange */
/*--------------------------------------------------------------------*/
void IBMAudio::InitAudioControl ()
{
      audioControl.ioctl_request =  AUDIO_IGNORE;
      audioControl.request_info =   NULL;
      audioControl.position =       0;
      audioControl.return_code =    0xDeadBeaf;
} /* IBMAudio::InitAudioControl */
/*------------------------------------------------------------------------*/

Generated by  Doxygen 1.6.0   Back to index